package io.terminus.paas.profiler.monitor

import com.alibaba.dubbo.rpc.Invocation
import com.alibaba.dubbo.rpc.Result
import com.alibaba.dubbo.rpc.RpcContext
import io.terminus.paas.profiler.model.TracingRecord
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeoutException

/**
 * Created by gengrong on 16/5/18.
 */
abstract class AbstractMonitor : Monitor {

    private val recordMapper = ConcurrentHashMap<String, TracingRecord>()

    private val SERVICE_ID = "SERVICE_ID";
    private val TRACING_ID = "TRACING_ID";
    private val ROOT_ID = "ROOT_ID";

    // TODO 比较乱
    override fun invokerBefore(invocation: Invocation) {
        var serviceId = RpcContext.getContext().getAttachment(SERVICE_ID);
        if (serviceId == null && invocation.getAttachment("interface") != null) {
            serviceId = invocation.getAttachment("interface");
            RpcContext.getContext().setAttachment(SERVICE_ID, serviceId);
        }

        val tracingId = UUID.randomUUID().toString()
        var record = TracingRecord()
        var interfaceName = invocation.getAttachment("interface")
        // injvm
        if (interfaceName == null) {
            interfaceName = invocation.invoker.url.path
        }

        var parentId = RpcContext.getContext().getAttachment(TRACING_ID);

        var rootId = RpcContext.getContext().getAttachment(ROOT_ID);
        if (rootId == null) {
            rootId = tracingId
        }

        record.startTime = System.currentTimeMillis()
        record.tracingId = tracingId
        record.parentId = parentId
        record.interfaceName = interfaceName
        record.methodName = invocation.methodName
        record.rootId = rootId

        RpcContext.getContext().setAttachment(TRACING_ID, tracingId);
        RpcContext.getContext().setAttachment(ROOT_ID, rootId);
        recordMapper.put(tracingId, record)
    }

    override fun invokerAfter(result: Result) {
        var record = recordMapper.get(RpcContext.getContext().getAttachment(TRACING_ID))
        record ?: throw IllegalArgumentException("tracing record is null")
        record.endTime = System.currentTimeMillis()
        record.status = if (result.hasException()) {
            if (result.exception is TimeoutException)
                TracingRecord.STATUS.TIME_OUT
            else
                TracingRecord.STATUS.EXCEPTION
        } else {
            TracingRecord.STATUS.OK
        }
        recording(record);
    }
}